package org.ftz.JudgeOnline.question;
import org.apache.log4j.Logger;
import org.ftz.control.DBUtil;
import org.ftz.control.util;
import org.ftz.pojo.Testcase;
import java.io.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.concurrent.*;
public class ExecuteCLangService {
    private static final Logger log = Logger.getLogger(ExecuteCLangService.class);
    public static final String CODE_PATH = "D:/BigProgram/"; //代码存放路径
    private boolean generateCFile(String content){ //C代码
        BufferedWriter out = null;
        try{
            FileOutputStream fileOutputStream = new FileOutputStream(CODE_PATH + "test.cpp", false);//写入
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream);
            out = new BufferedWriter(outputStreamWriter);
            out.write(content);
        }catch (Exception e){
            log.error(e.getCause().getMessage());
            return false;
        }finally {
            try {
                out.close();
            }catch (IOException e){
                log.error(e.getCause().getMessage());
                return false;
            }
        }
        return true;
    }
    public int findFile(String path) { //在指定路径中查找以input开头的文件个数
        int input=0;
        final File file = new File(path);
        if (file != null){
            final File[] files = file.listFiles();
            for (int i = 0; i < files.length; i++) {
                if (files[i].isDirectory()) {
                    findFile(files[i].getAbsolutePath());
                }else{
                    if(files[i].getName().startsWith("input")){
                        input++;
                    }
                }
            }
        }
        return input;
    }
    public Testcase selectTestById(int id){ //根据题目编号返回测试数据
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        Testcase testcase = new Testcase();
        try {
            connection = DBUtil.getConnection();  // 1. 建立连接
            String sql = "select * from oj_test where id=?"; // 2. 拼装 SQL 语句
            statement = connection.prepareStatement(sql);
            statement.setInt(1,id);
            resultSet = statement.executeQuery(); // 3. 执行 SQL 语句
            while (resultSet.next()){ // 4. 遍历结果集
                testcase.setId(resultSet.getInt("id"));
                testcase.setInput(resultSet.getString("input"));
                testcase.setOutput(resultSet.getString("output"));
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            DBUtil.close(connection,statement,resultSet); // 5. 关闭释放资源
        }
        return testcase;
    }
    public void writeToTxt(String content,String filename) throws IOException { //将查询的测试数据写到文件中
        File file1=new File("D:\\test");
        if(!file1.exists()){
            file1.mkdir();
        }
        File file=new File("D:\\test\\"+filename);
        FileOutputStream f1=new FileOutputStream(file);
        byte []buff=content.getBytes();
        try {
            f1.write(buff);
        }catch (Exception e){
        }finally {
            f1.close();
        }
    }
    public String txt2String(String path){ //读取文件内容
        StringBuilder result = new StringBuilder();
        try {
            BufferedReader br = new BufferedReader(new FileReader(new File(path))); //构造一个BufferedReader类来读取文件
            String s = null;
            while((s = br.readLine()) != null) { //使用readLine方法，一次读一行
                result.append(s+System.lineSeparator());
            }
            br.close();
        } catch(Exception e) {
            e.printStackTrace();
        }
        return result.toString().trim();
    }

    public String runCLangCode(String sourceCode,int id){ //源代码
        boolean b = generateCFile(sourceCode); //先生成文件
        System.out.println(b);
        Executor executor = Executors.newSingleThreadExecutor();
        FutureTask<String> futureTask = new FutureTask<String>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                String compileResult = execCmd("g++ "+CODE_PATH+"test.cpp "+"-o "+CODE_PATH+"test",null); //编译C文件
                System.out.println("hello");
                if (compileResult.equals("")){
                    Testcase testcase = selectTestById(id);
                    String input=testcase.getInput().substring(0,testcase.getInput().length()-1);
                    String output=testcase.getOutput().substring(0,testcase.getOutput().length()-1);
                    System.out.println("编译成功"); //编译不出错的情况，replaceAll将\n换成HTML的换行，空格换成HTML的空格
                    String path="D:\\test";
                    String[] split1 = input.split("@");
                    for(int i=0;i<split1.length;i++){
                        String filename="input"+(i+1)+".txt";
                        writeToTxt(split1[i],filename);
                    }
                    String[] split2 = output.split("@");
                    for(int i=0;i<split2.length;i++){
                        String filename="output"+(i+1)+".txt";
                        writeToTxt(split2[i],filename);
                    }
                    int cnt = findFile(path);
                    for(int i=0;i<cnt;i++){
                        String paths="D:\\test\\output"+(i+1)+".txt";
                        String cmd="cmd /c " + CODE_PATH+"test.exe"+" < D:\\test\\input"+(i+1)+".txt";
                        String myanswer = execCmd(cmd, null);
                        System.out.println(myanswer.trim());
                        String trueanswer = txt2String(paths);
                        System.out.println(trueanswer);
                        System.out.println(myanswer.trim().equals(trueanswer));
                        if(!myanswer.trim().equals(trueanswer)){
                            util.deleteAllFile(path);
                            return "答案错误";
                        }
                    }
                    util.deleteAllFile(path);
                    return "成功";
                } else {
                    System.out.println("编译出错");  //编译出错，找到error的位置，返回error及其后的信息compileResult.substring(errorIndex)
                    int errorIndex = compileResult.indexOf("error");
                    util.deleteAllFile("D:\\test");
                    return "编译出错";
                }
            }
        });
        executor.execute(futureTask);
        try {
            execCmd("taskkill /f /im test.exe",null); //编译运行完毕将text.exe的进程kill
            log.info("killed test.exe");
        }catch (Exception e){
            e.printStackTrace();
        }
        String result = "";
        try{
            result=futureTask.get(10, TimeUnit.SECONDS); //设置超时时间
        }catch (InterruptedException e) {
            log.info("Interrupt");
            result = "程序中断，请检查是否有内存冲突等错误";
            // future.cancel(true);
        }catch (ExecutionException e) {
            result = "程序执行错误";
            futureTask.cancel(true);
        }catch (TimeoutException e) {
            result = "时间超限，请检查是否存在无限循环等程序无法自动结束的情况";
        }
        log.info("result - "+result);
        return result.equals("")?"没有输出":result;
    }

    private String execCmd(String cmd, File dir) throws Exception { //执行系统命令, 返回执行结果
        StringBuilder result = new StringBuilder();
        Process process = null;
        BufferedReader bufrIn = null;
        BufferedReader bufrError = null;
        String command = cmd; //加入输入
        try {
            process = Runtime.getRuntime().exec(command, null, dir); // 执行命令, 返回一个子进程对象（命令在子进程中执行）
            new Thread(){  //用另一个线程输入参数
                public void run(){
                }
            };
            process.waitFor(); // 方法阻塞, 等待命令执行完成（成功会返回0）
            InputStreamReader inputStreamReader = new InputStreamReader(process.getInputStream(), "UTF-8");  // 获取命令执行结果, 有两个结果: 正常的输出 和 错误的输出（PS: 子进程的输出就是主进程的输入）
            bufrIn = new BufferedReader(inputStreamReader);
            InputStreamReader inputStreamReader1 = new InputStreamReader(process.getErrorStream(), "UTF-8");
            bufrError = new BufferedReader(inputStreamReader1);
            String line = null;  // 读取输出
            while ((line = bufrIn.readLine()) != null) {
                result.append(line).append('\n');
            }
            while ((line = bufrError.readLine()) != null) {
                result.append(line).append('\n');
            }

        } finally {
            closeStream(bufrIn);
            closeStream(bufrError);
            if (process != null) { // 销毁子进程
                process.destroy();
            }
        }
        return result.toString(); // 返回执行结果
    }
    private void closeStream(Closeable stream) {
        if (stream != null) {
            try {
                stream.close();
            } catch (Exception e) {
            }
        }
    }
}
